/*******************************************************************************
 * Copyright (c) 2010 European Software Institute - Tecnalia.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Author - Adri�n Noguero (adrian.noguero@esi.es)
 *     
 *******************************************************************************/
package es.esi.gemde.modeltransformator.service;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.HashMap;
import java.util.List;

import org.eclipse.emf.ecore.EClass;

import es.esi.gemde.core.resources.IGemdeResource.ResourceType;
import es.esi.gemde.modeltransformator.ModelTransformatorPlugin;
import es.esi.gemde.modeltransformator.exceptions.NoSuchEngineException;
import es.esi.gemde.modeltransformator.service.impl.EngineRepository;
import es.esi.gemde.modeltransformator.service.impl.Transformation;

/**
 * A factory to create {@link ITransformation} objects. ITransformations created with this class
 * will be correct by construction.
 *
 * @author Adrian Noguero (adrian.noguero@esi.es)
 * @version 1.0
 * @since 1.0
 *
 */
public class TransformationFactory {

	/**
	 * A static method to create an {@link ITransformation} instance.
	 * 
	 * @param name the name of the transformation that will be created.
	 * @param engineName the name of the engine to which this transformation will be related.
	 * @param transformationURIs the list of {@link URI} defining the transformation resources.
	 * @param description the textual description of the transformation. Can be null.
	 * @param inputs the types of the inputs required by this transformation.
	 * @return the created {@link ITransformation} instance
	 * @throws IllegalArgumentException if a non-valid transformation name or a non-valid transformation file are provided.
	 * @throws NoSuchEngineException if the provided engine is not registered in the repository.
	 * @throws IOException if the factory couldn't open a connection to the transformation URI.
	 */
	public static ITransformation createTransformation (String name, String engineName, List<URI> transformationURIs, String description, List<EClass> inputs) throws IllegalArgumentException, NoSuchEngineException, IOException {
		// Check that the name is valid
		checkCorrectness(name, engineName, transformationURIs, description, inputs);
		return new Transformation(name, EngineRepository.getInstance().getEngineByName(engineName), transformationURIs, description, ResourceType.NORMAL, inputs);
	}
	
	/**
	 * A static method to create a session protected {@link ITransformation} instance.
	 * 
	 * @param name the name of the transformation that will be created.
	 * @param engineName the name of the engine to which this transformation will be related.
	 * @param transformationURIs the list of {@link URI} defining the transformation resources.
	 * @param description the textual description of the transformation. Can be null.
	 * @param inputs the types of the inputs required by this transformation.
	 * @return the created {@link ITransformation} instance
	 * @throws IllegalArgumentException if a non-valid transformation name or a non-valid transformation file are provided.
	 * @throws NoSuchEngineException if the provided engine is not registered in the repository.
	 * @throws IOException if the factory couldn't open a connection to the transformation URI.
	 * 
	 * @deprecated. Not intended to be used, only for internal resources management.
	 */
	public static ITransformation createProtectedTransformation (String name, String engineName, List<URI> transformationURIs, String description, List<EClass> inputs) throws IllegalArgumentException, NoSuchEngineException, IOException {
		// Check that the name is valid
		checkCorrectness(name, engineName, transformationURIs, description, inputs);
		return new Transformation(name, EngineRepository.getInstance().getEngineByName(engineName), transformationURIs, description, ResourceType.PROTECTED, inputs);
	}
	
	/**
	 * A static method to create a networked {@link ITransformation} instance.
	 * 
	 * @param name the name of the transformation that will be created.
	 * @param engineName the name of the engine to which this transformation will be related.
	 * @param transformationURIs the list of {@link URI} defining the transformation resources.
	 * @param description the textual description of the transformation. Can be null.
	 * @param inputs the types of the inputs required by this transformation.
	 * @return the created {@link ITransformation} instance
	 * @throws IllegalArgumentException if a non-valid transformation name or a non-valid transformation file are provided.
	 * @throws NoSuchEngineException if the provided engine is not registered in the repository.
	 * @throws IOException if the factory couldn't open a connection to the transformation URI.
	 * 
	 * @deprecated. Not intended to be used, only for internal resources management.
	 */
	public static ITransformation createNetworkedTransformation (String name, String engineName, List<URI> transformationURIs, String description, List<EClass> inputs) throws IllegalArgumentException, NoSuchEngineException, IOException {
		// Check that the name is valid
		checkCorrectness(name, engineName, transformationURIs, description, inputs);
		return new Transformation(name, EngineRepository.getInstance().getEngineByName(engineName), transformationURIs, description, ResourceType.NETWORKED, inputs);
	}

	/**
	 * A static method to create an {@link ITransformation} instance.
	 * 
	 * @param name the name of the transformation that will be created.
	 * @param engineName the name of the engine to which this transformation will be related.
	 * @param transformationURIs the list of {@link URI} defining the transformation resources.
	 * @param description the textual description of the transformation. Can be null.
	 * @param inputs the names and types of the inputs required by this transformation as a map.
	 * @return the created {@link ITransformation} instance
	 * @throws IllegalArgumentException if a non-valid transformation name or a non-valid transformation file are provided.
	 * @throws NoSuchEngineException if the provided engine is not registered in the repository.
	 * @throws IOException if the factory couldn't open a connection to the transformation URI.
	 */
	public static ITransformation createTransformation (String name, String engineName, List<URI> transformationURIs, String description, HashMap<String, EClass> inputs) throws IllegalArgumentException, NoSuchEngineException, IOException {
		// Check that the name is valid
		checkCorrectness(name, engineName, transformationURIs, description, inputs);
		return new Transformation(name, EngineRepository.getInstance().getEngineByName(engineName), transformationURIs, description, ResourceType.NORMAL, inputs);
	}
	
	/**
	 * A static method to create a session protected {@link ITransformation} instance.
	 * 
	 * @param name the name of the transformation that will be created.
	 * @param engineName the name of the engine to which this transformation will be related.
	 * @param transformationURIs the list of {@link URI} defining the transformation resources.
	 * @param description the textual description of the transformation. Can be null.
	 * @param inputs the names and types of the inputs required by this transformation as a map.
	 * @return the created {@link ITransformation} instance
	 * @throws IllegalArgumentException if a non-valid transformation name or a non-valid transformation file are provided.
	 * @throws NoSuchEngineException if the provided engine is not registered in the repository.
	 * @throws IOException if the factory couldn't open a connection to the transformation URI.
	 * 
	 * @deprecated. Not intended to be used, only for internal resources management.
	 */
	public static ITransformation createProtectedTransformation (String name, String engineName, List<URI> transformationURIs, String description, HashMap<String, EClass> inputs) throws IllegalArgumentException, NoSuchEngineException, IOException {
		// Check that the name is valid
		checkCorrectness(name, engineName, transformationURIs, description, inputs);
		return new Transformation(name, EngineRepository.getInstance().getEngineByName(engineName), transformationURIs, description, ResourceType.PROTECTED, inputs);
	}
	
	/**
	 * A static method to create a networked {@link ITransformation} instance.
	 * 
	 * @param name the name of the transformation that will be created.
	 * @param engineName the name of the engine to which this transformation will be related.
	 * @param transformationURIs the list of {@link URI} defining the transformation resources.
	 * @param description the textual description of the transformation. Can be null.
	 * @param inputs the names and types of the inputs required by this transformation as a map.
	 * @return the created {@link ITransformation} instance
	 * @throws IllegalArgumentException if a non-valid transformation name or a non-valid transformation file are provided.
	 * @throws NoSuchEngineException if the provided engine is not registered in the repository.
	 * @throws IOException if the factory couldn't open a connection to the transformation URI.
	 * 
	 * @deprecated. Not intended to be used, only for internal resources management.
	 */
	public static ITransformation createNetworkedTransformation (String name, String engineName, List<URI> transformationURIs, String description, HashMap<String, EClass> inputs) throws IllegalArgumentException, NoSuchEngineException, IOException {
		// Check that the name is valid
		checkCorrectness(name, engineName, transformationURIs, description, inputs);
		return new Transformation(name, EngineRepository.getInstance().getEngineByName(engineName), transformationURIs, description, ResourceType.NETWORKED, inputs);
	}
	
	/**
	 * A static method to create an {@link ITransformation} instance.
	 * 
	 * @param name the name of the transformation that will be created.
	 * @param engineName the name of the engine to which this transformation will be related.
	 * @param transformationURIs the list of {@link URI} defining the transformation resources.
	 * @param description the textual description of the transformation. Can be null.
	 * @param inputs the names and types of the inputs required by this transformation as a map.
	 * @param outputs the names and types of the outputs required by this transformation as a map.
	 * @return the created {@link ITransformation} instance
	 * @throws IllegalArgumentException if a non-valid transformation name or a non-valid transformation file are provided.
	 * @throws NoSuchEngineException if the provided engine is not registered in the repository.
	 * @throws IOException if the factory couldn't open a connection to the transformation URI.
	 */
	public static ITransformation createTransformation (String name, String engineName, List<URI> transformationURIs, String description, HashMap<String, EClass> inputs, HashMap<String, EClass> outputs) throws IllegalArgumentException, NoSuchEngineException, IOException {
		// Check that the name is valid
		checkCorrectness(name, engineName, transformationURIs, description, inputs);

		if (inputs == null) {
			throw new IllegalArgumentException("A null inputs types list was provided");
		}

		if (outputs == null) {
			throw new IllegalArgumentException("A null outputs types list was provided");
		}

		return new Transformation(name, EngineRepository.getInstance().getEngineByName(engineName), transformationURIs, description, ResourceType.NORMAL, inputs, outputs);
		
	}
	
	/**
	 * A static method to create a session protected {@link ITransformation} instance.
	 * 
	 * @param name the name of the transformation that will be created.
	 * @param engineName the name of the engine to which this transformation will be related.
	 * @param transformationURIs the list of {@link URI} defining the transformation resources.
	 * @param description the textual description of the transformation. Can be null.
	 * @param inputs the names and types of the inputs required by this transformation as a map.
	 * @param outputs the names and types of the outputs required by this transformation as a map.
	 * @return the created {@link ITransformation} instance
	 * @throws IllegalArgumentException if a non-valid transformation name or a non-valid transformation file are provided.
	 * @throws NoSuchEngineException if the provided engine is not registered in the repository.
	 * @throws IOException if the factory couldn't open a connection to the transformation URI.
	 * 
	 * @deprecated. Not intended to be used, only for internal resources management.
	 */
	public static ITransformation createProtectedTransformation (String name, String engineName, List<URI> transformationURIs, String description, HashMap<String, EClass> inputs, HashMap<String, EClass> outputs) throws IllegalArgumentException, NoSuchEngineException, IOException {
		// Check that the name is valid
		checkCorrectness(name, engineName, transformationURIs, description, inputs);
		
		if (inputs == null) {
			throw new IllegalArgumentException("A null inputs types list was provided");
		}
		
		if (outputs == null) {
			throw new IllegalArgumentException("A null outputs types list was provided");
		}
		
		return new Transformation(name, EngineRepository.getInstance().getEngineByName(engineName), transformationURIs, description, ResourceType.PROTECTED, inputs, outputs);
	}
	
	/**
	 * A static method to create a networked {@link ITransformation} instance.
	 * 
	 * @param name the name of the transformation that will be created.
	 * @param engineName the name of the engine to which this transformation will be related.
	 * @param transformationURIs the list of {@link URI} defining the transformation resources.
	 * @param description the textual description of the transformation. Can be null.
	 * @param inputs the names and types of the inputs required by this transformation as a map.
	 * @param outputs the names and types of the outputs required by this transformation as a map.
	 * @return the created {@link ITransformation} instance
	 * @throws IllegalArgumentException if a non-valid transformation name or a non-valid transformation file are provided.
	 * @throws NoSuchEngineException if the provided engine is not registered in the repository.
	 * @throws IOException if the factory couldn't open a connection to the transformation URI.
	 * 
	 * @deprecated. Not intended to be used, only for internal resources management.
	 */
	public static ITransformation createNetworkedTransformation (String name, String engineName, List<URI> transformationURIs, String description, HashMap<String, EClass> inputs, HashMap<String, EClass> outputs) throws IllegalArgumentException, NoSuchEngineException, IOException {
		// Check that the name is valid
		checkCorrectness(name, engineName, transformationURIs, description, inputs);
		
		if (inputs == null) {
			throw new IllegalArgumentException("A null inputs types list was provided");
		}
		
		if (outputs == null) {
			throw new IllegalArgumentException("A null outputs types list was provided");
		}
		
		return new Transformation(name, EngineRepository.getInstance().getEngineByName(engineName), transformationURIs, description, ResourceType.NETWORKED, inputs, outputs);
	}
	
	private static void checkCorrectness(String name, String engineName,	List<URI> transformationURIs, String description, List<EClass> inputs) throws IllegalArgumentException, IOException, NoSuchEngineException {
		// Check that the name is valid
		if (name == null || name.equals("") || ModelTransformatorPlugin.getService().transformationExists(name)) {
			throw new IllegalArgumentException("An invalid transformation name was provided");
		}

		if (inputs == null) {
			throw new IllegalArgumentException("A null inputs types list was provided");
		}

		// Check that the validation engine exists
		ITransformationEngine engine = EngineRepository.getInstance().getEngineByName(engineName);

		if (engine == null) {
			throw new NoSuchEngineException("The provided engine name cannot be located in the Engine Repository");
		}

		// Check if the provided uri is a valid one
		for (URI transformationURI : transformationURIs) {
			try {
				transformationURI.toURL().openStream();
			}
			catch (MalformedURLException mue) {
				throw new IllegalArgumentException("The provided URI couldn't be transformed to a URL");
			}
		}

		if (!engine.checkTransformationURIValidity(transformationURIs)) {
			throw new IllegalArgumentException("Provided transformation file is not a valid one");
		}
	}

	static private void checkCorrectness (String name, String engineName, List<URI> transformationURIs, String description, HashMap<String, EClass> inputs) throws IllegalArgumentException, NoSuchEngineException, IOException {
		// Check that the name is valid
		if (name == null || name.equals("") || ModelTransformatorPlugin.getService().transformationExists(name)) {
			throw new IllegalArgumentException("An invalid transformation name was provided");
		}

		if (inputs == null) {
			throw new IllegalArgumentException("A null inputs types list was provided");
		}

		// Check that the validation engine exists
		ITransformationEngine engine = EngineRepository.getInstance().getEngineByName(engineName);

		if (engine == null) {
			throw new NoSuchEngineException("The provided engine name cannot be located in the Engine Repository");
		}

		// Check if the provided uri is a valid one
		for (URI transformationURI : transformationURIs) {
			try {
				transformationURI.toURL().openStream();
			}
			catch (MalformedURLException mue) {
				throw new IllegalArgumentException("The provided URI couldn't be transformed to a URL");
			}
		}

		if (!engine.checkTransformationURIValidity(transformationURIs)) {
			throw new IllegalArgumentException("Provided transformation file is not a valid one");
		}
	}
	
}
